﻿// AvoidAreas.cpp : 实现文件
//

#include "stdafx.h"
#include "GHCY.h"
#include "Ground.h"
#include "../Hui/Hui.h"
#include "Car.h"
#include "GException.h"
#include "AvoidAreas.h"

// AvoidAreas

#ifdef _DEBUG
	#define new DEBUG_NEW
#endif


/// <作者>Mr. Hu</作者>
/// <日期>20250926</日期>
/// <功能>AGV的终点与本区相同</功能>
/// <参数 name = "pCar">指定AGV</参数>
/// <返回>true 相同，否则不同</返回>
/// <说明>如果区域没有关联站点，返回1， 如果pCar没有站点返回1</说明>
AVOID_AREA_SAME AvoidArea::same(Car* pCar)
{
	HLock hl2(&pCar->m_mutexPath, 1, (DWORD)-26);			// 等待读，退出时自动解锁

	if (!*bufCode)
		return AVOID_AREA_NONE;

	if (!pCar->m_pEndPoint)
		return AVOID_AREA_NONE;

	if (pCar->m_pEndPoint->m_bufCode[1] == bufCode[1])	// 关联第二个字符
		return AVOID_AREA_END;

	PathNode& pn = pCar->m_arrPath.GetAt(0);
	EndPoint* pep = theApp.m_pGround->m_nodesColl.getEndPoint(&pn);
	if (pep && pep->m_bufCode[1] == bufCode[1])			// 关联第二个字符
		return AVOID_AREA_FIRST;

	return AVOID_AREA_NONE;
}


// ======== 上下两个不同的类 ========


/// <作者>Mr. Hu</作者>
/// <日期>20250816</日期>
/// <功能>避让区集合</功能>
/// <参数></参数>
/// <返回></返回>
/// <说明></说明>
AvoidAreas::AvoidAreas()
{
}

//AvoidAreas::~AvoidAreas()
//{
//	for (size_t i = 0, length = getSize(); i < length; i++)
//	{
//		delete m_pArrCards[i];
//	}
//}

/// <作者>Mr. Hu</作者>
/// <日期>20250816</日期>
/// <功能>读取避让区</功能>
/// <参数></参数>
/// <返回></返回>
/// <说明></说明>
void AvoidAreas::load()
{
	CString strsql = L"SELECT `id`, `areaID`, `agvNumber`, `x`, `y`, `bufCode`"
		L" FROM `avoidarea`;";
	HRecordset rs(theApp.getDatabase());
	rs.Open2(strsql);

	//m_arrAvoidArea.SetSize(0, 300);	// 预留100个位置，避免内存碎片

	// 取字段名
	//CODBCFieldInfo fi;
	//rs.GetODBCFieldInfo( (short)5, fi );

	AvoidArea aa;
	int nAreaID = 1;
	int aid = nAreaID;
	int nMinX = MAXINT;
	int nMaxX = 0;
	int nMinY = MAXINT;
	int nMaxY = 0;
	int nAgvNum = 0;
	CString strCode;
	CDBVariant var;	// var 读取二进制数据时要释放
	while (!rs.IsEOF())
	{
		HLock hGeoCreate(&afxMutexGeo, -1, (DWORD)-29);

		CD2DPathGeometry* pGeo = new CD2DPathGeometry(&theApp.m_RenderTarget);
		pGeo->Create(&theApp.m_RenderTarget);
		ASSERT(pGeo->IsValid());		//???? 异常
		CD2DGeometrySink sink(*pGeo);	//?? 内存会不会越来越多
		ASSERT(sink.IsValid());			//???? 异常

		for (size_t i = 0; aid == nAreaID; i++)
		{
			// 读数据
			rs.GetFieldValue((short)3, var);
			int x = var.m_lVal;

			rs.GetFieldValue((short)4, var);
			int y = var.m_lVal;

			D2D1_POINT_2F pt = { (float)x, (float)y };
			pt = theApp.m_pGround->m_mtxCoordinates.TransformPoint(pt);

			if (!i)		// 只取第一条记录，其它记录相同
			{
				sink.BeginFigure(pt, D2D1_FIGURE_BEGIN_FILLED);

				rs.GetFieldValue((short)2, var);
				nAgvNum = var.m_lVal;

				rs.GetFieldValue((short)5, strCode);
			}
			else
				sink.AddLine(pt);

			if (x < nMinX)
				nMinX = x;
			if (x > nMaxX)
				nMaxX = x;
			if (y < nMinY)
				nMinY = y;
			if (y > nMaxY)
				nMaxY = y;

			rs.MoveNext();

			if (rs.IsEOF())
				break;

			rs.GetFieldValue((short)1, var);	// areaID
			aid = var.m_lVal;
		}

		sink.EndFigure(D2D1_FIGURE_END_CLOSED);
		sink.Close();

		aa.pGeoArea = pGeo;
		aa.nAreaID = nAreaID;
		aa.nAGVNumber = nAgvNum;
		int dx = nMaxX - nMinX;
		int dy = nMaxY - nMinY;
		aa.nRadius = (int)sqrt(dx * dx + dy * dy) / 2;
		int x = (nMaxX + nMinX) / 2;
		int y = (nMaxY + nMinY) / 2;
		D2D1_POINT_2F pt = { (float)x, (float)y };
		pt = theApp.m_pGround->m_mtxCoordinates.TransformPoint(pt);
		aa.nX = pt.x;
		aa.nY = pt.y;

		if (strCode.GetLength() >= 4)
		{
			aa.bufCode[0] = strCode[0];
			aa.bufCode[1] = strCode[1];
			aa.bufCode[2] = strCode[2];
			aa.bufCode[3] = strCode[3];
		}

		m_arrAvoidArea.Add(aa);

		nAreaID = aid;
	}
	//var.Clear();	// var 要释放
	rs.Close();
}

/// <作者>Mr. Hu</作者>
/// <日期>20220705</日期>
/// <功能>取端点数量</功能>
/// <参数 name = ""></参数>
/// <返回>端点总数</返回>
/// <说明></说明>
INT_PTR AvoidAreas::getSize()
{
	return m_arrAvoidArea.GetCount();
}

/// <作者>Mr. Hu</作者>
/// <日期>20250817</日期>
/// <功能>移除占用的AGV</功能>
/// <参数 pCar>指定AGV</参数>
/// <返回></返回>
/// <说明></说明>
void AvoidAreas::removeAGV(Car* pCar)
{
	for (size_t i = 0, length = getSize(); i < length; i++)
	{
		if (pCar == m_arrAvoidArea[i].pCar)
			m_arrAvoidArea[i].pCar = 0;
	}
}

/// <作者>Mr. Hu</作者>
/// <日期>20250817</日期>
/// <功能>避让</功能>
/// <参数 name = "pCar">指定AGV</参数>
/// <返回>true 避让，否则不避让</返回>
/// <说明></说明>
bool AvoidAreas::avoid(Car* pCar)
{
	ASSERT(pCar);

	for (size_t i = 0, length = getSize(); i < length; i++)
	{
		AvoidArea& aa = m_arrAvoidArea[i];

		bool bSame = (AVOID_AREA_FIRST != aa.same(pCar));		// 如果从关联站点取货出来，不占用，因为会影响进入的AGV

		// 判断当前AGV是否已经驶出
		if (aa.pCar == pCar)
		{
			ASSERT(aa.nAGVNumber);								// 一定是1
			UINT r = aa.pCar->compareArea(aa.pGeoArea);			// EXT + EXT2
			if (WORD(r) == D2D1_GEOMETRY_RELATION_DISJOINT)
				aa.pCar = 0;									// 已经驶出，清0，后半部分可能还在避让区内
			else if (!bSame)		// 如果从关联站点取货出来，不占用，因为会影响进入的AGV
				aa.pCar = 0;

			continue;
		}

		if (!bSame)
			//return 0;
#pragma 测试有没有问题
			continue;
#pragma 算法不对
		//int mindis = aa.nRadius + int(pCar->m_fGeoLength) + 2000;
		//int dis = pCar->calDistance(aa.nX, aa.nY);
		//if (dis > mindis)
		//	continue;											// 距离较远，不避让

		// 判断转弯区与避让区是否相交
		HLock hl2(&pCar->m_mutexPath, 1, (DWORD)-26);			// 等待读，退出时自动解锁
		ASSERT(pCar->m_nRunIndex);
		PathNode& pn = pCar->m_arrPath[pCar->m_nRunIndex];
		if (pn.m_pLockGeometry && pn.m_pTurnGeometry)
		{
			D2D1_GEOMETRY_RELATION rr = aa.pGeoArea->CompareWithGeometry(*pn.m_pTurnGeometry, pn.m_mtxRegion, 100.f);
			if (rr != D2D1_GEOMETRY_RELATION_DISJOINT)
			{
				if (!aa.nAGVNumber)								// 不允许AGV进入
				{
					ASSERT(!aa.pCar);
					pCar->m_pAvoidCar = 0;						// 0允许时，避让
					return 1;
				}

				if (aa.pCar)
				{
					pCar->m_pAvoidCar = aa.pCar;				// 避让区已经被占，避让
					return 1;
				}

				aa.pCar = pCar;									// 暂时占用避让区
			}
		}

		// 判断AGV与避让区是否相交
		UINT r = pCar->compareArea(aa.pGeoArea);
		bool bExt = GETWORD(r, 1) != D2D1_GEOMETRY_RELATION_DISJOINT;
		bool bExt2 = GETWORD(r, 0) != D2D1_GEOMETRY_RELATION_DISJOINT;
		if (!bExt2)
			continue;											// 不相交，不避让，后半部分可能还在避让区内

		if (!aa.nAGVNumber)
		{
			//if (!bExt)											// 排除减速区
			//	continue;

			ASSERT(!aa.pCar);
			pCar->m_pAvoidCar = 0;								// 0允许时避让
			return 1;
		}

		if (!aa.pCar)
		{
			aa.pCar = pCar;										// 设置占用AGV
			continue;
		}
		else if (bExt)
		{
			aa.pCar = pCar;										// 车体相交，优先占用
			continue;
		}
		else
		{
			pCar->m_pAvoidCar = aa.pCar;							// 避让
			return 1;
		}
	}

	return 0;
}
